package mybatis.session.defaults;

import mybatis.exception.MyBatisException;
import mybatis.executor.Executor;
import mybatis.executor.SimpleExecutor;
import mybatis.plugins.transaction.SpringManagedTransactionFactory;
import mybatis.session.Configuration;
import mybatis.session.SqlSession;
import mybatis.session.SqlSessionFactory;
import mybatis.session.TransactionIsolationLevel;
import mybatis.transaction.Transaction;
import mybatis.transaction.TransactionFactory;
import mybatis.transaction.jdbc.JdbcTransactionFactory;
import mybatis.transaction.managed.ManagedTransactionFactory;

import java.sql.SQLException;

public class DefaultSqlSessionFactory implements SqlSessionFactory {

    private final Configuration configuration;

    public DefaultSqlSessionFactory(Configuration configuration) {
        this.configuration = configuration;
    }

    @Override
    public SqlSession openSession() {
        return openSessionFromDataSource(null, false);
    }

    @Override
    public SqlSession openSession(boolean autoCommit) {
        return openSessionFromDataSource(null, autoCommit);
    }

    @Override
    public SqlSession openSession(TransactionIsolationLevel level) {
        return openSessionFromDataSource(level, false);
    }

    @Override
    public Configuration getConfiguration() {
        return configuration;
    }

    private SqlSession openSessionFromDataSource(TransactionIsolationLevel level, boolean autoCommit) {
        Transaction transaction = null;
        try {
            TransactionFactory transactionFactory = getTransactionFactory();
            transaction = transactionFactory.newTransaction(configuration.getDataSource(), level, autoCommit);
            Executor executor = new SimpleExecutor(configuration, transaction);
            return new DefaultSqlSession(configuration, executor, autoCommit);
        } catch (Exception e) {
            closeTransaction(transaction);
            throw new MyBatisException("Session 创建错误。原因：" + e, e);
        }
    }

    private void closeTransaction(Transaction tx) {
        if (tx != null) {
            try {
                tx.close();
            } catch (SQLException ignore) {
            }
        }
    }

    private TransactionFactory getTransactionFactory() {
        switch (this.configuration.getTransactionType()) {
            case JDBC:
                return new JdbcTransactionFactory();
            case MANAGED:
                return new ManagedTransactionFactory();
            case SPRING:
                return new SpringManagedTransactionFactory();
            default:
                throw new MyBatisException("事务管理器工厂无效，请检查配置。");
        }
    }
}
